unit Python;

interface

uses
  SysUtils;

const
  pythondll = 'python25.dll';
  APIVersion = 1013;

  //PyMethodDef.ml_flags
  METH_VARARGS  = $0001;
  METH_KEYWORDS = $0002;

type
  EPyException = class(Exception);
  PPyObject = ^PyObject;
  PPyTypeObject = ^PyTypeObject;
  pydestructor = procedure(ob: PPyObject); cdecl;
  PyCFunction = function (self, args:PPyObject): PPyObject; cdecl;

  PyObject = packed record
    ob_refcnt: Integer;
    ob_type:   PPyTypeObject;
  end;

  PyTypeObject = packed record
    ob_refcnt: Integer;
    ob_type: PPyTypeObject;
    ob_size: Integer;
    tp_name: PChar;
    tp_basicsize, tp_itemsize: Integer;
    tp_dealloc: pydestructor;
  end;

  PPyMethodDef = ^PyMethodDef;
  PyMethodDef  = packed record
     name:  PChar;
     func:  PyCFunction;
     flg: Integer;
     doc:   PChar;
  end;

function fromArgs (args: PPyObject; format: string;
                   argp: array of Pointer; var ResToPython: PPyObject): Boolean;
procedure fromRes (args: PPyObject; format: string;
                   argp: array of Pointer);
function call (func: PPyObject; args: array of PPyObject): PPyObject;

function PyErr_Occurred: PPyObject; cdecl; external pythondll;
procedure PyErr_Print; cdecl; external pythondll;

function str(str: PWideChar; size: Integer): PPyObject; cdecl; external pythondll name 'PyUnicodeUCS2_FromWideChar'; overload;
function str(s: WideString): PPyObject; overload;
function str(s: string): PPyObject; overload;
function int(x: LongInt): PPyObject; cdecl; external pythondll name 'PyInt_FromLong';
function ptr(x: Pointer): PPyObject;
function bool(x: LongInt): PPyObject; cdecl; external pythondll name 'PyBool_FromLong'; overload;
function bool(x: Boolean): PPyObject; overload;
function nul: PPyObject;
function tuple(arr: array of PPyObject): PPyObject;

procedure ref(op: PPyObject);
procedure dref(op: PPyObject); overload;
procedure dref(args: array of PPyObject); overload;

function IsCallable(obj: PPyObject): Integer; cdecl; external pythondll name 'PyCallable_Check';
function Py_Call(ob, args: PPyObject): PPyObject; cdecl; external pythondll name 'PyObject_CallObject';

function Run(str: PChar): Integer; cdecl; external pythondll name 'PyRun_SimpleString';

procedure Initialize; cdecl; external pythondll name 'Py_Initialize';
procedure Finalize; cdecl; external pythondll name 'Py_Finalize';
function InitModule( const name : PChar; md : PPyMethodDef) : PPyObject;
function Import(name: PPyObject): PPyObject; cdecl; external pythondll name 'PyImport_Import';
function Module_GetDict(modl: PPyObject): PPyObject; cdecl; external pythondll name 'PyModule_GetDict';
function Dict_GetItemStr(dp: PPyObject; key: PChar): PPyObject; cdecl; external pythondll name 'PyDict_GetItemString';


implementation

function Py_InitModule4(name: PChar; methods: PPyMethodDef; doc: PChar;
                        passthrough: PPyObject; Api_Version: Integer):
                        PPyObject; cdecl; external pythondll name 'Py_InitModule4';
function DLL_PyArg_Parse(args: PPyObject; format: PChar): Integer; cdecl;
                        external pythondll name 'PyArg_Parse'; 
function DLL_PyArg_ParseTuple(args: PPyObject; format: PChar):
                        Integer; cdecl; external pythondll name 'PyArg_ParseTuple';
function PyTuple_New(size: Integer): PPyObject; cdecl; external pythondll name 'PyTuple_New';
function PyTuple_SetItem(ob: PPyObject; key: Integer; value: PPyObject):
                        Integer; cdecl; external pythondll name 'PyTuple_SetItem';


function str(s: WideString): PPyObject;
  begin Result := str(PWideChar(s), Length(s)); end;
function str(s: string): PPyObject;
  begin Result := str(WideString(s)); end;
function ptr(x: Pointer): PPyObject;
  begin Result := int(Integer(x)); end;
function bool(x: Boolean): PPyObject;
  begin
    if x then Result := bool(1)
    else Result := bool(0);
  end;
function nul: PPyObject;
  begin Result := str('') end;
function tuple(arr: array of PPyObject): PPyObject;
  var I, Size: Integer;
  begin
    //references of arguments were stolen here.
    Size := Length(arr);
    Result := PyTuple_New(Size);
    for I := 0 to Size - 1 do begin
      PyTuple_SetItem(Result, I, arr[I]);
    end;
  end;

function InitModule( const name : PChar; md : PPyMethodDef) : PPyObject;
begin
  result := Py_InitModule4( name, md, nil, nil, APIVersion );
end;

procedure ref(op: PPyObject);
  begin
    Inc(op^.ob_refcnt);
  end;

procedure dref(op: PPyObject);
  begin
    if Assigned(op) then begin
      with op^ do begin
        Dec(ob_refcnt);
        if ob_refcnt = 0 then begin
          ob_type^.tp_dealloc(op);
        end;
      end;
    end;
  end;

procedure dref(args: array of PPyObject); overload;
  var I: Integer;
  begin
    for I := 0 to Length(args) - 1 do begin
      dref(args[I]);
    end;
  end;

function PyArg_Parse (args: PPyObject; format: PChar;
                      argp: array of Pointer): Integer; cdecl;
  begin
    Result := 0;
    asm
      lea edx, format
      push [edx]

      sub edx, TYPE PChar
      push [edx]

      call DLL_PyArg_Parse

      pop edx
      pop edx
      mov Result, eax
    end;
  end;


function PyArg_ParseTuple (args: PPyObject; format: PChar;
                           argp: array of Pointer): Integer; cdecl;
  begin
    Result := 0;
    asm
      lea edx, format
      push [edx]

      sub edx, TYPE PChar
      push [edx]

      call DLL_PyArg_ParseTuple

      pop edx
      pop edx
      mov Result, eax
    end;
  end;

function fromArgs (args: PPyObject; format: string;
                   argp: array of Pointer; var ResToPython: PPyObject): Boolean;
  var ok: Integer;
  begin
    ok := PyArg_ParseTuple(args, PChar(format), argp);
    if 0 = ok then begin
      PyErr_Print();
      ResToPython := str('Error: argument''s type does not match.');
      Result := False;
    end else begin
      Result := True;
    end;
  end;

procedure fromRes (args: PPyObject; format: string;
                   argp: array of Pointer);
  var ok: Integer;
  begin
    ok := PyArg_Parse(args, PChar(format), argp);
    //dref(args);
    if 0 = ok then begin
      PyErr_Print();
      raise EPyException.Create('Error: result''s type does not match.');
    end;
  end;

function call (func: PPyObject; args: array of PPyObject): PPyObject;
  begin
    Result := Py_Call(func, tuple(args));
    dref(args);
    if not Assigned(Result) then begin
      PyErr_Print();
      raise EPyException.Create('Failed to call function.')
    end;
  end;

end.
